package it.polito.connectedcar.tcc;

import it.polito.appeal.traci.ControlledLinks;
import it.polito.appeal.traci.Lane;
import it.polito.appeal.traci.TrafficLight;
import it.polito.appeal.traci.connectedcar.trafficlight.ControlledLink;
import it.polito.connectedcar.trafficlight.ConnectedTLProgram;
import it.polito.connectedcar.trafficlight.ConnectedTLProgram.ConnectedTLPhase;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * This class models a Traffic Light representation for a TCC.<br/>
 * A Traffic Light is described by a set of controlled lanes and links along
 * with its position and program characteristics
 * 
 * @author Nicola Aresta
 * 
 */
public class TCCTrafficLightAbstraction {

	private String trafficLightId;

	/**
	 * the position of the traffic Light is not yest implemented ( needs to find
	 * a way to retrieve it , because Traci does not support it )
	 */
	private Point2D position;
	private Set<String> controlledLanes;
	private Map<Integer, List<ControlledLink>> controlledLinks;
	private int currentPhase;
	private int nextPhase;
	private long lastChangeTimeStamp;
	private long estimatedPhaseChangeTime;
	private ConnectedTLProgram program;

	/**
	 * trasfrom the SUMO traffic Light objects into the TCC managed traffic
	 * light object
	 * 
	 * @param tl
	 * @throws IOException
	 */
	public TCCTrafficLightAbstraction(TrafficLight tl) throws IOException {

		this.program = new ConnectedTLProgram(tl.queryReadCompleteDefinition().get());

		ControlledLinks cLinks = tl.queryReadControlledLinks().get();
		it.polito.appeal.traci.ControlledLink[][] clArrays = cLinks.getLinks();
		controlledLinks = new HashMap<Integer, List<ControlledLink>>();

		for (int j = 0; j < clArrays.length; j++) {
			List<ControlledLink> cList = new ArrayList<ControlledLink>();
			for (int k = 0; k < clArrays[j].length; k++) {
				ControlledLink cLink = new ControlledLink(clArrays[j][k]
						.getIncomingLane().getID(), clArrays[j][k]
						.getOutgoingLane().getID(), clArrays[j][k]
						.getAcrossLane().getID());
				cList.add(cLink);
			}
			controlledLinks.put(j, cList);
		}
		controlledLanes = new HashSet<String>();
		List<Lane> lanes = tl.queryReadControlledLanes().get();
		for (Lane lane : lanes) {
			controlledLanes.add(lane.getID());
		}
	}

	/**
	 * Given a laneId , return the current signal present on that input laneId,
	 * when it is supposed to change and which is the next signal's state
	 * 
	 * @param laneId
	 * @return
	 */
	public TCCTrafficLightPhaseChange getTCCTrafficLightPhaseChange(String laneId) {
		for (Entry<Integer, List<ControlledLink>> entry : controlledLinks.entrySet()) {
			for (ControlledLink cl : entry.getValue()) {
				if (cl.getIncomingLane().equals(laneId)) {
					TCCTrafficLightPhaseChange tlps = new TCCTrafficLightPhaseChange();
					List<ConnectedTLPhase> phases = program.getLogics().get(0).getPhases();
					ConnectedTLPhase currentTLPhase = phases.get(currentPhase);
					String currentStateChar = String.valueOf(currentTLPhase.getState().charAt(entry.getKey()));
					tlps.setCurrentState(currentStateChar);

					tlps.setLastChangeTimeStamp(lastChangeTimeStamp);
					tlps.setPosition(getPosition());

					int nextPhaseIndex = currentPhase;
					long assumedTimeToChangeColor = 0;
					String nextState;
					long partialDuration = estimatedPhaseChangeTime;

					do {
						assumedTimeToChangeColor += partialDuration;
						nextPhaseIndex = (nextPhaseIndex == phases.size() - 1)? 0: nextPhaseIndex + 1;
						ConnectedTLPhase nextPhase = phases.get(nextPhaseIndex);
						nextState = String.valueOf(nextPhase.getState().charAt(entry.getKey()));
						partialDuration = nextPhase.getDuration();

					} while (nextState.equalsIgnoreCase(currentStateChar));

					tlps.setAssumedTimeToSwitch(assumedTimeToChangeColor);
					tlps.setNextState(nextState);
					return tlps;
				}
			}
		}
		return null;
	}

	public Point2D getPosition() {
		return position;
	}

	public void setPosition(Point2D position) {
		this.position = position;
	}

	public Set<String> getControlledLanes() {
		return controlledLanes;
	}

	public void setControlledLanes(Set<String> controlledLanes) {
		this.controlledLanes = controlledLanes;
	}

	public int getCurrentPhase() {
		return currentPhase;
	}

	public void setCurrentPhase(int currentPhase) {
		this.currentPhase = currentPhase;
	}

	public int getNextPhase() {
		return nextPhase;
	}

	public void setNextPhase(int nextPhase) {
		this.nextPhase = nextPhase;
	}

	public long getEstimatedPhaseChangeTime() {
		return estimatedPhaseChangeTime;
	}

	public void setEstimatedPhaseChangeTime(long estimatedPhaseChangeTime) {
		this.estimatedPhaseChangeTime = estimatedPhaseChangeTime;
	}

	public String getTrafficLightId() {
		return trafficLightId;
	}

	public void setTrafficLightId(String trafficLightId) {
		this.trafficLightId = trafficLightId;
	}

	public ConnectedTLProgram getProgram() {
		return program;
	}

	public void setProgram(ConnectedTLProgram program) {
		this.program = program;
	}

	@Override
	public boolean equals(Object arg0) {
		if (arg0 == null)
			return false;
		return arg0.toString().equals(trafficLightId);
	}

	public long getLastChangeTimeStamp() {
		return lastChangeTimeStamp;
	}

	public void setLastChangeTimeStamp(long lastChangeTimeStamp) {
		this.lastChangeTimeStamp = lastChangeTimeStamp;
	}

}
